<script lang="ts">
	import {
		Background,
		BackgroundVariant,
		ConnectionMode,
		Controls,
		MiniMap,
		Panel,
		SvelteFlow,
		useSvelteFlow,
		type Node as FlowNode,
		type Edge as FlowEdge,
		type Connection,
		MarkerType
	} from '@xyflow/svelte';
	import '@xyflow/svelte/dist/style.css';
	import './flow.css';
	import NodeTypeIndexPanel from './panels/NodeTypeIndexPanel.svelte';
	import { roadmapStore } from './store.svelte';
	import EditNodeTool from './EditNodeTool.svelte';
	import JobDataNode from './nodes/roadmap/JobDataNode.svelte';
	import SkillDataNode from './nodes/roadmap/SkillDataNode.svelte';
	import ActivityDataNode from './nodes/roadmap/ActivityDataNode.svelte';
	import CertificateDataNode from './nodes/roadmap/CertificateDataNode.svelte';
	import StudyResourceDataNode from './nodes/roadmap/StudyResourceDataNode.svelte';
	import LearnStepDataNode from './nodes/roadmap/LearnStepDataNode.svelte';
	import KnowledgePointDataNode from './nodes/roadmap/KnowledgePointDataNode.svelte';
	import CharacterDataNode from './nodes/roadmap/CharacterDataNode.svelte';
	import RelateRoadmapDataNode from './nodes/roadmap/RelateRoadmapDataNode.svelte';
	import InterviewSkillDataNode from './nodes/roadmap/InterviewSkillDataNode.svelte';
	import InnerResourceDataNode from './nodes/roadmap/InnerResourceDataNode.svelte';
	import EduLevelDataNode from './nodes/roadmap/EduLevelDataNode.svelte';
	import MajorDataNode from './nodes/roadmap/MajorDataNode.svelte';

	import { getNodeType } from './nodes/types.svelte';
	import { callApi } from '$lib/utils/call_utils';
	import { appStore } from '$lib/stores/app.svelte';
	import { userStore } from '$lib/stores/user.svelte';
	import {
		EDGE_TYPE,
		EdgeKey as RoadmapEdgeKey,
		EdgeInfo as RoadmapEdgeInfo
	} from '$lib/api/proto/roadmap_data_api';
	import CommonEdge from './edges/CommonEdge.svelte';
	import EditEdgeModal from './modals/EditEdgeModal.svelte';
	import NodeStatePanel from './panels/NodeStatePanel.svelte';
	import MemberListPanel from './panels/MemberListPanel.svelte';
	import RagChatPanel from './panels/RagChatPanel.svelte';

	interface Props {
		heightStyle: string;
	}
	const props: Props = $props();

	const nodeTypes = {
		JobDataNode: JobDataNode,
		InterviewSkillDataNode: InterviewSkillDataNode,
		SkillDataNode: SkillDataNode,
		CertificateDataNode: CertificateDataNode,
		ActivityDataNode: ActivityDataNode,
		InnerResourceDataNode: InnerResourceDataNode,
		StudyResourceDataNode: StudyResourceDataNode,
		LearnStepDataNode: LearnStepDataNode,
		KnowledgePointDataNode: KnowledgePointDataNode,
		CharacterDataNode: CharacterDataNode,
		RelateRoadmapDataNode: RelateRoadmapDataNode,
		EduLevelDataNode: EduLevelDataNode,
		MajorDataNode: MajorDataNode
	};

	const edgeTypes = {
		CommonEdge: CommonEdge
	};

	let nodes = $state([] as FlowNode[]);
	let edges = $state([] as FlowEdge[]);

	const { screenToFlowPosition } = useSvelteFlow();

	function initNodes() {
		const tmpNodeList: FlowNode<any>[] = roadmapStore.nodeList.map((item) => ({
			id: item.nodeId,
			type: getNodeType(item.content!),
			position: {
				x: item.x,
				y: item.y
			},
			width: item.w,
			height: item.h,
			data: item,
			selectable: true,
			deletable: roadmapStore.roadmapMode == 'edit',
			draggable: roadmapStore.roadmapMode == 'edit',
			connectable: roadmapStore.roadmapMode == 'edit'
		}));
		nodes = tmpNodeList;
	}

	function initEdges() {
		const tmpEdgeList: FlowEdge<any>[] = roadmapStore.edgeList.map((item) => ({
			id: `${item.edgeKey?.fromNodeId}-${item.edgeKey?.fromHandle}-${item.edgeKey?.toNodeId}-${item.edgeKey?.toHandle}`,
			type: 'CommonEdge',
			source: item.edgeKey!.fromNodeId,
			target: item.edgeKey!.toNodeId,
			sourceHandle: item.edgeKey!.fromHandle,
			targetHandle: item.edgeKey!.toHandle,
			data: item,
			selectable: roadmapStore.roadmapMode == 'edit',
			markerStart: item.basicInfo?.hasStartArrow
				? {
						type: MarkerType.ArrowClosed,
						width: 20,
						height: 20,
						color: 'var(--color-accent)'
					}
				: undefined,
			markerEnd: item.basicInfo?.hasEndArrow
				? {
						type: MarkerType.ArrowClosed,
						width: 20,
						height: 20,
						color: 'var(--color-accent)'
					}
				: undefined,
			zIndex: 20
		}));
		edges = tmpEdgeList;
	}

	async function moveNodes(nodes: FlowNode[]) {
		if (userStore.userSession == null) {
			return;
		}
		for (const node of nodes) {
			await callApi(
				appStore.roadmapDataApiClient.updateNodePosition({
					sessionId: userStore.userSession,
					roadmapId: roadmapStore.roadmapId,
					nodeId: node.id,
					x: node.position.x,
					y: node.position.y
				}).response
			);
			roadmapStore.onUpdateNode(node.id);
		}
	}

	async function removeEdges(edges: FlowEdge[]) {
		for (const edge of edges) {
			await roadmapStore.onRemoveEdge((edge.data as unknown as RoadmapEdgeInfo).edgeKey!);
		}
	}

	async function removeNodes(nodes: FlowNode[]) {
		for (const node of nodes) {
			await roadmapStore.onRemoveNode(node.id);
		}
	}

	function checkCreateEdge(conn: Connection) {
		if (roadmapStore.roadmapMode != 'edit') {
			return false;
		}
		//检查连接是否存在
		const index = roadmapStore.edgeList.findIndex(
			(item) =>
				item.edgeKey?.fromNodeId == conn.source &&
				item.edgeKey.fromHandle == conn.sourceHandle &&
				item.edgeKey.toNodeId == conn.target &&
				item.edgeKey.toHandle == conn.targetHandle
		);
		if (index != -1) {
			return false;
		}
		return conn;
	}

	async function createEdge(conn: Connection) {
		if (userStore.userSession == null) {
			return;
		}
		const edgeKey: RoadmapEdgeKey = {
			fromNodeId: conn.source,
			fromHandle: conn.sourceHandle ?? '',
			toNodeId: conn.target,
			toHandle: conn.targetHandle ?? ''
		};
		await callApi(
			appStore.roadmapDataApiClient.addEdge({
				sessionId: userStore.userSession,
				roadmapId: roadmapStore.roadmapId,
				edgeKey: edgeKey,
				basicInfo: {
					edgeType: EDGE_TYPE.EDGE_TYPE_BEZIER,
					hasStartArrow: false,
					hasEndArrow: true,
					label: ''
				}
			}).response
		);
		roadmapStore.onUpdateEdge(edgeKey);
	}

	$effect(() => {
		initNodes();
	});

	$effect(() => {
		initEdges();
	});
</script>

<div class="w-full {props.heightStyle}">
	<SvelteFlow
		{nodes}
		{nodeTypes}
		{edges}
		{edgeTypes}
		connectionMode={ConnectionMode.Loose}
		fitView={true}
		deleteKey={['Backspace', 'Delete']}
		onnodedragstop={(e) => {
			if (e.targetNode != null) {
				moveNodes([e.targetNode]);
			}
			if (e.nodes.length > 0) {
				moveNodes(e.nodes);
			}
		}}
		ondragover={(e) => {
			e.preventDefault();
			if (e.dataTransfer) {
				e.dataTransfer.effectAllowed = 'move';
			}
		}}
		ondrop={(e) => {
			if (roadmapStore.targetNodeType != null) {
				roadmapStore.targetDropPosition = screenToFlowPosition({ x: e.clientX, y: e.clientY });
			}
		}}
		ondelete={({ nodes, edges }) => {
			removeEdges(edges);
			removeNodes(nodes);
		}}
		onbeforeconnect={(conn) => checkCreateEdge(conn) as Connection}
		onconnect={(conn) => createEdge(conn)}
		onconnectstart={() => (roadmapStore.inConnect = true)}
		onconnectend={() => (roadmapStore.inConnect = false)}
	>
		<Background variant={BackgroundVariant.Cross}/>
		<Controls showLock={false} position="top-right" orientation="horizontal" />
		<MiniMap
			nodeStrokeWidth={3}
			pannable={true}
			zoomable={true}
			zoomStep={1}
			position="bottom-left"
		/>
		{#if roadmapStore.roadmapMode == 'edit'}
			<Panel position="top-left">
				<NodeTypeIndexPanel />
			</Panel>
		{:else if roadmapStore.roadmapMode == 'update' && roadmapStore.ragConfigList.length > 0}
			<Panel position="top-left">
				<RagChatPanel />
			</Panel>
		{:else if roadmapStore.roadmapMode == 'view'}
			<Panel position="top-left">
				<MemberListPanel />
			</Panel>
		{/if}
		{#if (roadmapStore.roadmapMode == 'update' || roadmapStore.roadmapMode == 'view') && roadmapStore.selectNodeIdList.length == 1}
			<Panel position="bottom-right">
				<NodeStatePanel />
			</Panel>
		{/if}
	</SvelteFlow>
</div>

<div class="absolute -left-[10000px]">
	<EditNodeTool />
	{#if roadmapStore.editEdge != null}
		<EditEdgeModal />
	{/if}
</div>
